package com.jweb.framework.service.sqlmap.support;
import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
 * 修改人： Thomas
 * 修改内容：新增 
 * 类说明：数据操作工具类
 */
public final class SqlSessionUtils {
	
	private static final Log log = LogFactory.getLog(SqlSessionUtils.class);
	
	public static SqlSession getSqlSession(SqlSessionFactory sqlSessionFactory) {
		ExecutorType executorType = sqlSessionFactory.getConfiguration().getDefaultExecutorType();
		return getSqlSession(sqlSessionFactory , executorType , null);
	}
	
	public static SqlSession getSqlSession(SqlSessionFactory sqlSessionFactory , ExecutorType executorType , PersistenceExceptionTranslator persistenceExceptionTranslator){
		
		Assert.notNull(sqlSessionFactory , "No SqlSessionFactory specified");
		Assert.notNull(executorType , "No ExecutorType specified");
		
		SqlSessionHolder sqlSessionHolder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory); // sqlSessionHolder=null
		
		if((sqlSessionHolder != null) && (sqlSessionHolder.isSynchronizedWithTransaction())){
			if(sqlSessionHolder.getExecutorType() != executorType){
				throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction.");
			}
			
			sqlSessionHolder.requested();
			
			if(log.isDebugEnabled()){
				log.debug("Fetched SqlSession [" + sqlSessionHolder.getSqlSession() +"] from current transaction ");
			}
			
			return sqlSessionHolder.getSqlSession();
		}
		
		DataSource dataSource = sqlSessionFactory.getConfiguration().getEnvironment().getDataSource();
		
		boolean bool = dataSource instanceof TransactionAwareDataSourceProxy ; // bool=false
		Connection connection ;
		try {
			connection = bool ? dataSource.getConnection() : DataSourceUtils.getConnection(dataSource); //第一次调DataSourceUtils的方法时需要花费较长时间。
			
		} catch (SQLException e) {
			throw new CannotGetJdbcConnectionException("Could not get JDBC Connection for SqlSession ", e);
		}
		
		if(log.isDebugEnabled()){
			log.debug("Creating SqlSession with JDBC Connection [" + connection + "]");
		}
		
		SqlSession sqlSession = sqlSessionFactory.openSession(executorType , connection);
		
		if(TransactionSynchronizationManager.isSynchronizationActive()){
			if(!(sqlSessionFactory.getConfiguration().getEnvironment().getTransactionFactory() instanceof SpringManagedTransactionFactory)
					&& (DataSourceUtils.isConnectionTransactional(connection , dataSource))){
				throw new TransientDataAccessResourceException("SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization ");
			}
			
			if(log.isDebugEnabled()){
				log.debug("Registering transaction synchronization for SqlSession [" + sqlSession +"]");
			}
			sqlSessionHolder = new SqlSessionHolder(sqlSession , executorType , persistenceExceptionTranslator);
			TransactionSynchronizationManager.bindResource(sqlSessionFactory , sqlSessionHolder);
			TransactionSynchronizationManager.registerSynchronization(new InnerA(sqlSessionHolder , sqlSessionFactory));
			
			sqlSessionHolder.setSynchronizedWithTransaction(true);
			sqlSessionHolder.requested();
		}
		else if(log.isDebugEnabled()) {
			log.debug("SqlSession [" + sqlSession + "] was not registered for synchronization because synchronization is not active.");
		}
		
		return sqlSession ;
	}
	
	
	public static void closeSqlSession(SqlSession sqlSession , SqlSessionFactory sqlSessionFactory) {
		Assert.notNull(sqlSession , "No sqlSession specified");
		Assert.notNull(sqlSessionFactory , "No sqlSessionFactory specified");
		
		SqlSessionHolder sqlSessionHolder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory);
		if(sqlSessionHolder != null && (sqlSessionHolder.getSqlSession() == sqlSession)){
			if(log.isDebugEnabled()){
				log.debug("Releasing transactional SqlSession [" + sqlSession +"]");
			}
			sqlSessionHolder.released();
		}
		else {
			if(log.isDebugEnabled()){
				log.debug("Closing no transaction SqlSession [" + sqlSession + "]");
			}
			sqlSession.close();
		}
	}
	
	
	public static boolean isSqlSessionTransactional(SqlSession sqlSession , SqlSessionFactory sqlSessionFactory) {
		Assert.notNull(sqlSession , "No SqlSession specified");
		Assert.notNull(sqlSessionFactory , "No SqlSessionFactory specified");
		
		SqlSessionHolder sqlSessionHolder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory);
		return (sqlSessionHolder != null) && (sqlSessionHolder.getSqlSession()==sqlSession);
	}
	
	
	private static final class InnerA extends TransactionSynchronizationAdapter {
		
		private final SqlSessionHolder sqlSessionHolder ;
		private final SqlSessionFactory sqlSessionFactory ;
		
		public InnerA(SqlSessionHolder sqlSessionHolder , SqlSessionFactory sqlSessionFactory){
			Assert.notNull(sqlSessionHolder, "Parameter 'holder' must be not null");
			Assert.notNull(sqlSessionFactory, "Parameter 'sessionFactory' must be not null");
			
			this.sqlSessionHolder = sqlSessionHolder ;
			this.sqlSessionFactory = sqlSessionFactory ;
		}
		
		public int getOrder() {
			return 999;
		}
		
		public void suspend(){
			TransactionSynchronizationManager.unbindResource(this.sqlSessionFactory);
		}
		
		public void resume(){
			TransactionSynchronizationManager.bindResource(this.sqlSessionFactory, this.sqlSessionHolder);
		}
		
		public void beforeCommit(boolean bool) {
			if(!TransactionSynchronizationManager.isActualTransactionActive()) return ;
			try {
				if(SqlSessionUtils.log.isDebugEnabled()){
					SqlSessionUtils.log.debug("Transaction synchronization committing SqlSession [" + this.sqlSessionHolder.getSqlSession() +"]");
				}
				this.sqlSessionHolder.getSqlSession().commit();
			} catch (PersistenceException e) {
				if(this.sqlSessionHolder.getPersistenceExceptionTranslator() != null){
					throw this.sqlSessionHolder.getPersistenceExceptionTranslator().translateExceptionIfPossible(e);
				}
				throw e ;
			}
		}
		
		public void afterCompletion(int status) {
			if(!this.sqlSessionHolder.isOpen()){
				TransactionSynchronizationManager.unbindResource(this.sqlSessionFactory);
				try {
					if(SqlSessionUtils.log.isDebugEnabled()){
						SqlSessionUtils.log.debug("Transaction synchronization closing SqlSession [" + this.sqlSessionHolder.getSqlSession() + "]");
					}
					this.sqlSessionHolder.getSqlSession().close();
				} finally {
					this.sqlSessionHolder.reset();
				}
			}
		}
		
	}
	
	
}
